package com.xiguaji.tiktok.task.ranking.event;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import com.google.common.eventbus.AllowConcurrentEvents;
import com.google.common.eventbus.Subscribe;
import com.gugee.tiktok.common.model.dal.blogger.BloggerRecordRepository;
import com.gugee.tiktok.common.model.dal.blogger.BloggerTypeRepository;
import com.gugee.tiktok.common.model.mysql.BloggerMain;
import com.gugee.tiktok.common.model.mysql.BloggerType;
import com.gugee.tiktok.common.model.ranking.Ranking;
import com.xiguaji.boot.eventbus.Event;
import com.xiguaji.boot.eventbus.IEventListener;
import com.xiguaji.tiktok.task.ranking.RankingService;
import com.xiguaji.tiktok.task.ranking.client.BloggerClient;
import lombok.extern.slf4j.Slf4j;
import lombok.val;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.List;

@Slf4j
@Component
public class FetchBloggerRankingEventListener implements IEventListener<FetchBloggerRankingEventListener.FetchBloggerRankingEvent> {

    @Autowired
    RankingService rankingService;

    @Autowired
    BloggerTypeRepository bloggerTypeRepository;

    @Resource(name ="BloggerClientRanking")
    BloggerClient bloggerClient;

    List<BloggerType> bloggerTypes = new ArrayList<>();

    @Autowired
    BloggerRecordRepository bloggerRecordRepository;

    @PostConstruct
    public void initConstruct(){
        BloggerType bloggerType = new BloggerType();
        bloggerType.setId(0);
        bloggerType.setName("ALL");
        this.bloggerTypes.add(bloggerType);
        this.bloggerTypes.addAll(bloggerTypeRepository.getBloggerTypeByIsDisplay(true));
    }

    @Override
    @Subscribe
    @AllowConcurrentEvents
    public void handle(FetchBloggerRankingEvent event) {

        Calendar calendar = Calendar.getInstance();

        //bloggerTypes.forEach( bloggerType -> dayRanking(bloggerType));
        // calendar.add(Calendar.DAY_OF_MONTH,-1);

        bloggerTypes.parallelStream().forEach(bloggerType -> {
            try{
                dayRanking(bloggerType,calendar);
            }catch (Exception e){
                log.error("<<<<<<博主日榜,{}分类出错",bloggerType.getName(),e);
            }
        });

        // 周统计
        if(calendar.get(Calendar.DAY_OF_WEEK) == 1){
            bloggerTypes.parallelStream().forEach(bloggerType -> {
                try{
                    weekRanking(bloggerType,calendar);
                }catch (Exception e){
                    log.error("<<<<<<博主周榜,{}分类出错",bloggerType.getName(),e);
                }
            });
        }
        // 月统计
        if(calendar.get(Calendar.DAY_OF_MONTH) == 1){
            bloggerTypes.parallelStream().forEach(bloggerType -> {
                try{
                    monthRanking(bloggerType,calendar);
                }catch (Exception e){
                    log.error("<<<<<<博主月榜,{}分类出错",bloggerType.getName(),e);
                }
            });
        }


    }


    /**
     * 每天排行榜
     * @param bloggerType
     */
    public void dayRanking(BloggerType bloggerType,Calendar date){
        long rankingStart = System.currentTimeMillis();
        List<BloggerMain> bloggerMains = bloggerClient.getBloggerRanking(bloggerType.getName().equals("ALL")?"":bloggerType.getName(),1000);
        if((System.currentTimeMillis()-rankingStart)/1000 >1)
            log.info("FetchBloggerRankingEventListener rankingType:"+bloggerType.getName()+" find use time:"+(System.currentTimeMillis()-rankingStart)/1000+"s");

        if(bloggerMains.size() >0){
            int curDay = Integer.parseInt(DateUtil.format(getBeforeDay(date,1),"yyyyMMdd"));
            int beforeDay = Integer.parseInt(DateUtil.format(getBeforeDay(date,2),"yyyyMMdd"));
            List<Ranking> beforeRankings = rankingService.getRankings(bloggerType.getId(),1,beforeDay);
            Map<Long,Ranking> beforeRankingMap = new HashMap<>();

            beforeRankings.forEach(b->beforeRankingMap.put(b.getUid(),b));
            List<Ranking> rankings = new ArrayList<>();
            int rankingSort = 0;

            for (int i = 0; i < bloggerMains.size(); i++) {
                try{
                    Ranking ranking = new Ranking();
                    ranking.setUid(bloggerMains.get(i).getOUid());
                    ranking.setBloggerId(bloggerMains.get(i).getId());
                    ranking.setBloggerType(bloggerType.getId());
                    ranking.setNickname(bloggerMains.get(i).getONickname());
                    ranking.setUniqueId(bloggerMains.get(i).getOUniqueId());
                    ranking.setEnterpriseVerifyReason(bloggerMains.get(i).getEnterpriseVerifyReason());
                    ranking.setCustomVerify(bloggerMains.get(i).getCustomVerify());
                    ranking.setRegion(bloggerMains.get(i).getORegion());
                    ranking.setFollower(bloggerMains.get(i).getOFollowerCount());
                    ranking.setAvatar(bloggerMains.get(i).getOAvatar168());
                    ranking.setScore(bloggerMains.get(i).getScore());
                    ranking.setLikes(bloggerMains.get(i).getOTotalFavorited());
                    ranking.setRankingType(1);
                    ranking.setBelong(curDay);
                    ranking.setRanking(rankingSort+1);
                    ranking.setAvgView(bloggerMains.get(i).getOAvgViews());
                    ranking.setBioUrl(bloggerMains.get(i).getBioUrl());
                    ranking.setBioEmail(bloggerMains.get(i).getBioEmail());
                    ranking.setWithFusionShopEntry(bloggerMains.get(i).getWithFusionShopEntry());
                    var beforeDayRanking = beforeRankingMap.get(bloggerMains.get(i).getOUid());
                    val optionalBloggerRecord = bloggerClient.findBloggerRecords(curDay, bloggerMains.get(i).getOUid());

                    if(optionalBloggerRecord.isPresent()){
                        ranking.setLikesGrowth(optionalBloggerRecord.get().getLikeTotalCountGrowth());
                        ranking.setReviewGrowth(optionalBloggerRecord.get().getReviewGrowth());
                        ranking.setShareGrowth(optionalBloggerRecord.get().getShareGrowth());
                        ranking.setViewGrowth(optionalBloggerRecord.get().getViewGrowth());
                        ranking.setFollowerGrowth(optionalBloggerRecord.get().getFollowerGrowth());
                    }else{ //博主没有日打点的数据
                        ranking.setLikesGrowth(0);
                        ranking.setReviewGrowth(0);
                        ranking.setShareGrowth(0);
                        ranking.setViewGrowth(0);
                        ranking.setFollowerGrowth(0);
                    }

                    //参与率
                    try{
                        BigDecimal dscCount = new BigDecimal(bloggerMains.get(i).getOAvgLikes()
                                +bloggerMains.get(i).getOAvgShare()+
                                bloggerMains.get(i).getOAvgReviews());
                        ranking.setEngagementRate((dscCount.divide(new BigDecimal(bloggerMains.get(i).getOAvgViews()),3, RoundingMode.FLOOR).multiply(new BigDecimal(100))).setScale(1).doubleValue());
                    }catch (Exception e){
                        ranking.setEngagementRate(0.0);
                    }

                    // 防止评论和分享为零
                    /*if(ranking.getReviewGrowth() == 0 || ranking.getShareGrowth() == 0){

                        try{
                            val optionalYesterdayBloggerRecord = bloggerClient.findBloggerRecords(beforeDay, bloggerMains.get(i).getOUid());
                            long yesterdayLikeGrowth = optionalYesterdayBloggerRecord.get().getLikeTotalCountGrowth();
                            ranking.setReviewGrowth(optionalBloggerRecord.get().getLikeTotalCountGrowth()/(yesterdayLikeGrowth/optionalYesterdayBloggerRecord.get().getReviewGrowth()));
                            ranking.setShareGrowth(optionalBloggerRecord.get().getLikeTotalCountGrowth()/(yesterdayLikeGrowth/optionalYesterdayBloggerRecord.get().getShareGrowth()));
                        }catch (Exception e){
                            continue;
                        }
                    }*/


                    if(null != beforeDayRanking){

                        if(ranking.getRanking() < beforeDayRanking.getRanking()){
                            ranking.setState(State.UP.getValue());
                        }else if(ranking.getRanking() > beforeDayRanking.getRanking()){
                            ranking.setState(State.DOWN.getValue());
                        }else{
                            ranking.setState(State.FLAT.getValue());
                        }
                        ranking.setPosition(beforeDayRanking.getRanking() - ranking.getRanking());
                    }else{
                        ranking.setPosition(0);
                        ranking.setState(State.NEW.getValue());
                    }
                    ranking.setCreateBy("FetchBloggerRanking - Day -Ranking");
                    ranking.setUpdateBy("FetchBloggerRanking - Day -Ranking");
                    rankingSort+=1;
                    rankings.add(ranking);
                }catch (Exception e){
                    log.error("FetchBloggerRanking error uid:{}",bloggerMains.get(i).getOUid(),e);
                    continue;
                }
            }

            if(rankings.size() > 0){
                long rankingSaveStart = System.currentTimeMillis();
                rankingService.saveAll(rankings);
                if((System.currentTimeMillis()-rankingSaveStart)/1000 >1)
                    log.info("FetchBloggerRankingEventListener rankingType:"+bloggerType.getName()+" save use time:"+(System.currentTimeMillis()-rankingSaveStart)/1000+"s");
            }
        }
    }

    public void weekRanking(BloggerType bloggerType,Calendar date){
        if(date.get(Calendar.DAY_OF_WEEK) != 1){
            return;
        }
        int beforeDay = Integer.parseInt(DateUtil.format(getBeforeDay(date,7),"yyyyMMdd"));
        int curDay = Integer.parseInt(DateUtil.format(getBeforeDay(date,1),"yyyyMMdd"));
        // 上周排行数据
        List<Ranking> curRankings = rankingService.getRankings(bloggerType.getId(),1,beforeDay,curDay);

        if(curRankings.size() > 0) {
            // 上两周排行数据
            List<Ranking> lastTimeRankings = rankingService.getRankings(bloggerType.getId(),2,
                    Integer.parseInt(DateUtil.format(getBeforeDay(date,14),"yyyyMMdd")));
            Map<Long,Ranking> beforeRankingMap = new HashMap<>();

            lastTimeRankings.forEach(b->beforeRankingMap.put(b.getUid(),b));
            List<Ranking> rankings = new ArrayList<>();

            for (int i = 0; i < curRankings.size(); i++) {
                Ranking ranking = curRankings.get(i);
                ranking.setRankingType(2);
                ranking.setBelong(beforeDay);
                ranking.setRanking(i+1);

                // 上周总增涨值
                /*val growthData = bloggerRecordRepository.findByGrowthData(ranking.getUid(), beforeDay, curDay);
                ranking.setReviewGrowth(growthData.getReviewGrowth());
                ranking.setShareGrowth(growthData.getShareGrowth());
                ranking.setLikesGrowth(growthData.getLikeGrowth());
                ranking.setViewGrowth(growthData.getViewGrowth());
                ranking.setFollowerGrowth(growthData.getFollowerGrowth());
                ranking.setAvgView(growthData.getAvgView());*/

                var beforeDayRanking = beforeRankingMap.get(ranking.getUid());

                if(null != beforeDayRanking){

                    if(ranking.getRanking() < beforeDayRanking.getRanking()){
                        ranking.setState(State.UP.getValue());
                    }else if(ranking.getRanking() > beforeDayRanking.getRanking()){
                        ranking.setState(State.DOWN.getValue());
                    }else{
                        ranking.setState(State.FLAT.getValue());
                    }
                    ranking.setPosition(beforeDayRanking.getRanking() - ranking.getRanking());
                }else{
                    ranking.setPosition(0);
                    ranking.setState(State.NEW.getValue());
                }
                ranking.setCreateBy("FetchBloggerRanking - Week -Ranking");
                ranking.setUpdateBy("FetchBloggerRanking - Week -Ranking");
                ranking.setId(0);
                rankings.add(ranking);
            }
            if(rankings.size() > 0){
                long rankingSaveStart = System.currentTimeMillis();
                rankingService.saveAll(rankings);
                if((System.currentTimeMillis()-rankingSaveStart)/1000 >1)
                    log.info("FetchBloggerRankingEventListener rankingType:"+bloggerType.getName()+" save use time:"+(System.currentTimeMillis()-rankingSaveStart)/1000+"s");
            }
        }
    }

    public void monthRanking(BloggerType bloggerType, Calendar date){
        if(date.get(Calendar.DAY_OF_MONTH) != 1){
            return;
        }

        int beforeDay = Integer.parseInt(DateUtil.format(getBeforeMonth(date,1),"yyyyMMdd"));
        int curDay = Integer.parseInt(DateUtil.format(getBeforeDay(date,1),"yyyyMMdd"));
        // 上月排行数据
        List<Ranking> curRankings = rankingService.getRankings(bloggerType.getId(),1,beforeDay,curDay);

        if(curRankings.size() > 0) {
            // 上两月排行数据
            List<Ranking> lastTimeRankings = rankingService.getRankings(bloggerType.getId(),3,
                    Integer.parseInt(DateUtil.format(getBeforeMonth(date,2),"yyyyMMdd")));
            Map<Long,Ranking> beforeRankingMap = new HashMap<>();

            lastTimeRankings.forEach(b->beforeRankingMap.put(b.getUid(),b));
            List<Ranking> rankings = new ArrayList<>();

            // 上月总天数
            int dayCount = DateUtil.calendar(DateUtil.parse(String.valueOf(beforeDay),"yyyyMMdd")).getActualMaximum(Calendar.DAY_OF_MONTH);

            for (int i = 0; i < curRankings.size(); i++) {
                Ranking ranking = curRankings.get(i);
                ranking.setRankingType(3);
                ranking.setBelong(beforeDay);
                ranking.setRanking(i+1);
                var beforeDayRanking = beforeRankingMap.get(ranking.getUid());

                // 上周总增涨值
                /*val growthData = bloggerRecordRepository.findByGrowthData(ranking.getUid(), beforeDay, curDay);
                ranking.setReviewGrowth(growthData.getReviewGrowth());
                ranking.setShareGrowth(growthData.getShareGrowth());
                ranking.setLikesGrowth(growthData.getLikeGrowth());
                ranking.setViewGrowth(growthData.getViewGrowth());
                ranking.setFollowerGrowth(growthData.getFollowerGrowth());
                ranking.setAvgView(growthData.getAvgView());*/

                if(null != beforeDayRanking){
                    if(ranking.getRanking() < beforeDayRanking.getRanking()){
                        ranking.setState(State.UP.getValue());
                    }else if(ranking.getRanking() > beforeDayRanking.getRanking()){
                        ranking.setState(State.DOWN.getValue());
                    }else{
                        ranking.setState(State.FLAT.getValue());
                    }
                    ranking.setPosition(beforeDayRanking.getRanking() - ranking.getRanking());
                }else{
                    ranking.setPosition(0);
                    ranking.setState(State.NEW.getValue());
                }
                ranking.setCreateBy("FetchBloggerRanking - Month -Ranking");
                ranking.setUpdateBy("FetchBloggerRanking - Month -Ranking");
                ranking.setId(0);
                rankings.add(ranking);
            }
            if(rankings.size() > 0){
                long rankingSaveStart = System.currentTimeMillis();
                rankingService.saveAll(rankings);
                if((System.currentTimeMillis()-rankingSaveStart)/1000 >1)
                    log.info("FetchBloggerRankingEventListener rankingType:"+bloggerType.getName()+" save use time:"+(System.currentTimeMillis()-rankingSaveStart)/1000+"s");
            }
        }
    }

    private Date getBeforeDay(Calendar calendar,int day){
        Calendar now = ObjectUtil.clone(calendar);
        now.add(Calendar.DAY_OF_MONTH,-day);
        return now.getTime();
    }

    private Date getBeforeMonth(Calendar calendar,int month){
        Calendar now = ObjectUtil.clone(calendar);
        now.add(Calendar.MONTH,-month);
        return now.getTime();
    }

    private enum State {
        UP("UP"),
        DOWN("DOWN"),
        FLAT("FLAT"),
        NEW("NEW");

        State(String value){
            this.value = value;
        }
        private String value;

        public String getValue() {
            return value;
        }
    }

    public static class FetchBloggerRankingEvent extends Event{

        public FetchBloggerRankingEvent() {
            super("FetchBloggerRanking");
        }
    }
}
